/****************************************************************************
 *
 * Copyright (C) 2003-2008 Los Alamos National Security, LLC
 *                         Packet Analytics Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 2 as
 * published by the Free Software Foundation.  You may not use, modify or
 * distribute this program under any other version of the GNU General
 * Public License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 ****************************************************************************/
package nfse;

import java.sql.*;
import java.util.*;
import java.security.*;
import nfse.query.QueryDate;
import nfse.dataset.netflow.NetFlowThread;

/*******************************************************************************
 * Convinience class for obtaining system metadata information.
 * 
 * @author This code is the property of Los Alamos National Laboratory and the
 *         University of California, Copyright 2005
 */
public class MetaDatabase extends nfse.JDBCDatabase {

    public static MetaDatabase create() throws Exception {
        Properties props = new Properties();
        props.setProperty("user", NetFSE.METADATA_USER);
        props.setProperty("password", NetFSE.METADATA_PASSWORD);
        props.setProperty("client_encoding", "UTF8");

        Class.forName("org.postgresql.Driver");
        String url = "jdbc:postgresql://" + NetFSE.METADATA_HOST + "/" + NetFSE.METADATA_DATABASE;

        MetaDatabase mdb = new MetaDatabase(url, props);
        return mdb;
    }

    public MetaDatabase(String dbUrl, Properties props) throws Exception {
        super(dbUrl, props);
    }

    public void addDataFile(long fileID, int typeID, int sensorID, int nodeID, String path) {
        Statement s = createStatement();
        try {
            try {
                String sql = "insert into data_files values (" + fileID + "," + typeID + "," + sensorID + "," + nodeID
                        + ",0,0,'" + path + "','')";
                s.execute(sql);

            } catch (Exception e) {
                if (!e.getMessage().contains("duplicate key"))
                    e.printStackTrace();
            }

            ArrayList<String> inserts = new ArrayList<String>();
            String sql = "select * from App_List";
            ResultSet rs = s.executeQuery(sql);
            while (rs.next()) {
                inserts.add("insert into data_files_by_app values (" + fileID + "," + rs.getInt("App_ID") + ",0,0)");
            }
            rs.close();

            for (String insertSQL : inserts) {
                try {
                    s.execute(insertSQL);
                } catch (Exception e) {
                    if (!e.getMessage().contains("duplicate key"))
                        e.printStackTrace();
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                s.close();
            } catch (Exception e) {
                // igonre
            }
        }
    }

    public SyslogThread[] getSyslogThreads() {
        SyslogThread[] threads = null;
        try {
            Vector<SyslogThread> temp = new Vector<SyslogThread>();
            String sql = "select * from syslog_agent_conf";
            Statement s = createStatement();
            ResultSet rs = s.executeQuery(sql);
            while (rs.next()) {
                int sensor = rs.getInt("sensor");
                String ip = rs.getString("ip");
                if (ip == null)
                    ip = "";
                String filePath = rs.getString("file_path");
                String keyword = rs.getString("keyword");
                SyslogThread st = new SyslogThread(sensor, filePath, keyword, ip);
                temp.addElement(st);
            }
            rs.close();
            s.close();
            threads = new SyslogThread[temp.size()];
            for (int i = 0; i < threads.length; i++) {
                threads[i] = (SyslogThread) temp.elementAt(i);
            }
            return threads;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return threads;
    }

    public SyslogThread getSyslogThread(int sensorID) {
        SyslogThread thread = null;
        try {

            String sql = "select * from syslog_agent_conf where sensor=" + sensorID;
            Statement s = createStatement();
            ResultSet rs = s.executeQuery(sql);
            if (rs.next()) {
                int sensor = rs.getInt("sensor");
                String ip = rs.getString("ip");
                if (ip == null)
                    ip = "";
                String filePath = rs.getString("file_path");
                String keyword = rs.getString("keyword");
                thread = new SyslogThread(sensor, filePath, keyword, ip);

            }
            rs.close();
            s.close();

            return thread;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public NetFlowThread[] getNetFlowThreads() {
        NetFlowThread[] threads = null;
        try {
            Vector<NetFlowThread> temp = new Vector<NetFlowThread>();
            String sql = "select * from netflow_agent_conf";
            Statement s = createStatement();
            ResultSet rs = s.executeQuery(sql);
            while (rs.next()) {
                int sensor = rs.getInt("sensor");
                int port = rs.getInt("port");
                NetFlowThread st = new NetFlowThread(sensor, port);
                temp.addElement(st);
            }
            rs.close();
            s.close();
            threads = new NetFlowThread[temp.size()];
            for (int i = 0; i < threads.length; i++) {
                threads[i] = (NetFlowThread) temp.elementAt(i);
            }
            return threads;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return threads;
    }

    /***************************************************************************
     * Returns the distinct run numbers for a given date range and sensor.
     * 
     * @param startDate
     *            The lower bound of the date range to query
     * @param endDate
     *            The uppper bound of the date range to query
     * @param sensor
     *            The sensor for which to obtain run information
     * @return An array of integers representing the unique run numbers for the
     *         date range
     */
    public int[] getDistinctRuns(QueryDate startDate, QueryDate endDate, int sensor) {
        int[] result = new int[0];
        try {
            Vector<Integer> temp = new Vector<Integer>();
            Statement s = this.createStatement();
            String sql = "";
            if (endDate != null) {
                sql = "select distinct Run from Static_Indices where Sensor_ID=" + sensor + " and Index_Date between '"
                        + startDate.toMySQLString() + "' and '" + endDate.toMySQLString() + "' order by Run asc";
            } else {
                sql = "select distinct Run from Static_Indices where Sensor_ID=" + sensor + " and Index_Date='"
                        + startDate.toMySQLString() + "' order by Run asc";
            }

            System.out.println(sql);
            ResultSet rs = s.executeQuery(sql);
            if (rs != null) {
                while (rs.next()) {
                    temp.addElement(new Integer(rs.getInt("Run")));
                }
                rs.close();
            }

            s.close();
            result = new int[temp.size()];
            for (int i = 0; i < result.length; i++) {
                result[i] = temp.elementAt(i);
            }
        } catch (Exception E) {
            E.printStackTrace();
        }
        return result;
    }

    /***************************************************************************
     * Returns an array of Run objects for a given time range and sensor. Used
     * to determine what index structures to search for a time period for a
     * particular sensor.
     * 
     * @param startDate
     *            The lower bound of the date range to query
     * @param endDate
     *            The uppper bound of the date range to query
     * @param sensor
     *            The sensor for which to obtain run information
     * @return An array of Run objects indicating the indexes to search for this
     *         time period
     */
    public Run[] getRuns(QueryDate startDate, QueryDate endDate, int sensor) {
        Run[] result = new Run[0];
        try {
            if (endDate == null)
                endDate = startDate;
            Vector<Run> temp = new Vector<Run>();
            Statement s = this.createStatement();
            String sql = "select * from Static_Indices where Sensor_ID=" + sensor + " and (Min_Time >= "
                    + startDate.startTS / 1000 + " and Max_Time <=" + endDate.endTS / 1000 + ")"
                    + " group by Month, Day, Year, Run";
            ResultSet rs = s.executeQuery(sql);
            if (rs != null) {
                while (rs.next()) {
                    Run run = new Run();
                    run.setRun(rs.getInt("Run"));
                    run.setMinTime(rs.getLong("Min_Time"));
                    run.setMaxTime(rs.getLong("Max_Time"));
                    run.setMonth(rs.getInt("Month"));
                    run.setDay(rs.getInt("Day"));
                    run.setYear(rs.getInt("Year"));
                    run.setDate(nfse.query.QueryDate.processDate(rs.getString("Index_Date")));
                    temp.addElement(run);
                }
                rs.close();
            }
            s.close();
            result = new Run[temp.size()];
            for (int i = 0; i < result.length; i++) {
                result[i] = (Run) temp.elementAt(i);
            }
        } catch (Exception E) {
            E.printStackTrace();
        }
        return result;
    }

    /***************************************************************************
     * Returns an array of Run objects for a given time range and sensor. Used
     * to determine what index structures to search for a time period for a
     * particular sensor.
     * 
     * @param startTS
     *            The lower bound of the date range to query
     * @param endTS
     *            The uppper bound of the date range to query
     * @param sensor
     *            The sensor for which to obtain run information
     * @return An array of Run objects indicating the indexes to search for this
     *         time period
     */
    public Run[] getRuns(java.sql.Timestamp startTS, java.sql.Timestamp endTS, int sensor) {
        Run[] result = new Run[0];
        try {
            Vector<Run> temp = new Vector<Run>();
            Statement s = this.createStatement();
            String sql = "select * from Static_Indices where Field_ID=" + NetFSE.FIELD_START_TS + " and Sensor_ID="
                    + sensor + " and ((Min_Time between  " + startTS.getTime() / 1000 + " and " + endTS.getTime()
                    / 1000 + ") or (Max_Time between " + startTS.getTime() / 1000 + " and " + endTS.getTime() / 1000
                    + ") or (Min_Time <= " + startTS.getTime() / 1000 + " and Max_Time >= " + endTS.getTime() / 1000
                    + "))" + " ";
            System.out.println(sql);
            ResultSet rs = s.executeQuery(sql);
            if (rs != null) {
                while (rs.next()) {
                    Run run = new Run();
                    run.setRun(rs.getInt("Run"));
                    run.setMinTime(rs.getLong("Min_Time"));
                    run.setMaxTime(rs.getLong("Max_Time"));
                    run.setMonth(rs.getInt("Month"));
                    run.setDay(rs.getInt("Day"));
                    run.setYear(rs.getInt("Year"));
                    run.setDate(nfse.query.QueryDate.processDate(rs.getString("Index_Date")));
                    temp.addElement(run);
                }
                rs.close();
            }

            s.close();
            result = new Run[temp.size()];
            for (int i = 0; i < result.length; i++) {
                result[i] = (Run) temp.elementAt(i);
            }
        } catch (Exception E) {
            E.printStackTrace();
        }
        return result;
    }

    public ArrayList<TempTableRun> getTempTableRuns(java.sql.Timestamp startTS, java.sql.Timestamp endTS, int sensor) {
        ArrayList<TempTableRun> result = new ArrayList<TempTableRun>();
        try {
            Statement s = this.createStatement();
            String sql = "select * from Temp_Tables a, Nodes b where a.Node_ID=b.ID and a.Sensor_ID=" + sensor
                    + " and ( " + "  (Min_TS between  '" + startTS.toString() + "' and '" + endTS.toString() + "') "
                    + " or (Max_TS between '" + startTS.toString() + "' and '" + endTS.toString() + "') "
                    + " or (Min_TS <= '" + startTS.toString() + "' and Max_TS >= '" + endTS.toString()
                    + "') or (Min_TS is null) " + ") ";
            System.out.println(sql);

            ResultSet rs = s.executeQuery(sql);
            if (rs != null) {
                while (rs.next()) {
                    TempTableRun run = new TempTableRun();
                    run.setNodeID(rs.getInt("Node_ID"));
                    run.setHost(rs.getString("Name"));
                    run.setTable(rs.getString("Table_Name"));
                    run.setMinTS(rs.getTimestamp("Min_TS"));
                    run.setMaxTS(rs.getTimestamp("Max_TS"));
                    run.setDynamicUser(rs.getString("Dynamic_DB_User"));
                    run.setDynamicPassword(rs.getString("Dynamic_DB_Password"));
                    run.setDynamicPort(rs.getInt("Dynamic_DB_Port"));
                    run.setSensorID(sensor);
                    result.add(run);
                }
                rs.close();
            }

        } catch (Exception E) {
            E.printStackTrace();
        }
        return result;
    }

    /***************************************************************************
     * This method tries to authenticate the given user/password combination,
     * returning the user identifier from the system metadata upon success.
     * 
     * @param user
     *            The user name from the system metadata
     * @param pass
     *            The password for this user
     * @return The user identifier from the system metadata on success or -1 if
     *         authentication fails
     */
    public int authenticate(String user, String pass) {
        try {

            Statement s = this.createStatement();
            String sql = "select * from Users where User_Name='" + user + "'";
            ResultSet rs = s.executeQuery(sql);
            if (!rs.next()) {
                rs.close();
                s.close();
                return -1; // There was no matching user name
            }

            // Computed the hashed value of the pass parameter
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.digest(pass.getBytes());
            byte[] digest = md.digest(); // Compute the digest
            StringBuilder temp = new StringBuilder();
            for (int i = 0; i < digest.length; i++)
                temp.append(Integer.toHexString(digest[i]));
            String digestStr = temp.toString();

            // Compare the two passwords
            String password = rs.getString("Password");
            if (password.compareTo(digestStr) == 0) {
                int userID = rs.getInt("ID");
                rs.close();
                s.close();
                return userID; // Return the user ID, success
            }
            rs.close();
            s.close();
        } catch (Exception E) {
            E.printStackTrace();
        }
        return -1; // Failed to authenticate
    }

    /***************************************************************************
     * Helper method for returning IndexInfo for arbitrary SQL. Used in several
     * public methods below.
     * 
     * @param sql
     * @return An arry of IndexInfo objects of length 0 or more
     */
    private IndexInfo[] staticIndexInfo(String sql) {
        IndexInfo[] result = null;
        try {
            Statement s = connection.createStatement();
            ResultSet rs = s.executeQuery(sql);
            Vector<IndexInfo> temp = new Vector<IndexInfo>();
            while (rs.next()) {
                IndexInfo info = new IndexInfo();
                info.setSensorId(rs.getInt("Sensor_ID"));
                info.setMonth(rs.getInt("Month"));
                info.setDay(rs.getInt("Day"));
                info.setYear(rs.getInt("Year"));
                info.setRun(rs.getInt("Run"));
                info.setField(rs.getInt("Field_ID"));
                info.setDbPath(rs.getString("DB_Path"));
                info.setMaxTime(rs.getLong("Max_Time"));
                info.setMinTime(rs.getLong("Min_Time"));
                temp.addElement(info);
            }
            rs.close();
            s.close();
            result = new IndexInfo[temp.size()];
            for (int i = 0; i < result.length; i++) {
                result[i] = (IndexInfo) temp.elementAt(i);
            }
        } catch (Exception E) {
            E.printStackTrace();
        }
        return result;
    }

    /***************************************************************************
     * Finds static index information matching the criteria specifed in the
     * parameters or null if there is no match.
     * 
     * @param month
     *            Month of the index (1-12)
     * @param day
     *            Day of month of the index (1-31)
     * @param year
     *            Year of the index (YYYY)
     * @param run
     *            Run of the index (1-n)
     * @param field
     *            Field identifier of the index
     * @param sensor
     *            Sensor identifier of the index
     * @return IndexInfo matching the parameters or null if none could be found
     */
    public IndexInfo getStaticIndexInfo(int month, int day, int year, int run, int field, int sensor) {
        String sql = "select * from Static_Indices where Month=" + month + " and Day=" + day + " and Year=" + year
                + " and Run=" + run + " and Field_ID=" + field + " and Sensor_ID=" + sensor;

        return (staticIndexInfo(sql))[0];
    }

    /***************************************************************************
     * Finds dynamic index information matching the criteria specifed in the
     * parameters or null if there is no match.
     * 
     * @param month
     *            Month of the index (1-12)
     * @param day
     *            Day of month of the index (1-31)
     * @param year
     *            Year of the index (YYYY)
     * @param run
     *            Run of the index (1-n)
     * @param field
     *            Field identifier of the index
     * @param sensor
     *            Sensor identifier of the index
     * @return IndexInfo matching the parameters or null if none could be found
     */
    public IndexInfo getLocalIndexInfo(int node, int month, int day, int year, int run, int field, int sensor) {
        IndexInfo info = null;
        try {
            Statement s = connection.createStatement();
            String sql = "select * from Local_Indices where Month=" + month + " and Day=" + day + " and Year=" + year
                    + " and Run=" + run + " and Field_ID=" + field + " and Sensor_ID=" + sensor + " and Node_ID="
                    + node;

            ResultSet rs = s.executeQuery(sql);
            if (rs.next()) {
                info = new IndexInfo();
                info.setSensorId(rs.getInt("Sensor_ID"));
                info.setMonth(month);
                info.setDay(day);
                info.setYear(year);
                info.setRun(run);
                info.setField(field);
                info.setDataPath(rs.getString("Data_Path"));
                info.setDbPath(rs.getString("DB_Path"));
                info.setMaxTime(rs.getLong("Max_Time"));
                info.setMinTime(rs.getLong("Min_Time"));
            }
            rs.close();
            s.close();
        } catch (Exception E) {
            E.printStackTrace();
        }
        return info;
    }

    /***************************************************************************
     * Finds data file information matching the criteria specifed in the
     * parameters or null if there is no match.
     * 
     * @param month
     *            Month of the index (1-12)
     * @param day
     *            Day of month of the index (1-31)
     * @param year
     *            Year of the index (YYYY)
     * @param run
     *            Run of the index (1-n)
     * @param sensor
     *            Sensor identifier of the index
     * @return A DataInfo object matching the parameters or null if none could
     *         be found
     */
    public DataInfo getDataInfo(int month, int day, int year, int run, int sensor) {
        DataInfo info = null;
        try {
            Statement s = connection.createStatement();
            String sql = "select * from Data_Info where Month=" + month + " and Day=" + day + " and Year=" + year
                    + " and Run=" + run + " and Sensor_ID=" + sensor;

            ResultSet rs = s.executeQuery(sql);
            if (rs.next()) {
                info = new DataInfo();
                info.setSensor(sensor);
                info.setMonth(month);
                info.setDay(day);
                info.setYear(year);
                info.setRun(run);
                info.setDataPath(rs.getString("Path"));
                info.setMaxTime(rs.getLong("Max_Time"));
                info.setMinTime(rs.getLong("Min_Time"));
                info.setNumRecords(rs.getLong("Num_Lines"));
                info.setNumBytes(rs.getLong("Num_Bytes"));
                rs.close();
                s.close();
                return info;
            }
            rs.close();
            s.close();
        } catch (Exception E) {
            E.printStackTrace();
        }
        return null;
    }

    /***************************************************************************
     * Creates an entry in the system metadata for a dynamic index according to
     * the IndexInfo parameter.
     * 
     * @param indexInfo
     *            The index information of the dynamic index being added
     */
    public void addLocalIndex(IndexInfo indexInfo) {
        try {
            Statement s = connection.createStatement();
            String sql = "insert into Local_Indices values (" + indexInfo.getNodeId() + ", " + indexInfo.getSensorId()
                    + ", " + indexInfo.getField() + ", '" + indexInfo.getYear() + "-" + indexInfo.getMonth() + "-"
                    + indexInfo.getDay() + "', " + indexInfo.getRun() + ", " + indexInfo.getMonth() + ", "
                    + indexInfo.getDay() + ", " + indexInfo.getYear() + ", '" + indexInfo.getDbPath() + "', '"
                    + indexInfo.getDataPath() + "', " + indexInfo.getMinTime() + ", " + indexInfo.getMaxTime() + ")";
            s.execute(sql);
            s.close();
        } catch (Exception E) {
            E.printStackTrace();
        }
    }

    public void addTempTable(String tableName, int node, int sensor) {
        try {
            Statement s = connection.createStatement();
            String sql = "insert into Temp_Tables values (" + node + ", " + sensor + ", '" + tableName + "',null,null)";
            // System.out.println(sql);
            s.execute(sql);
            s.close();

        } catch (Exception E) {
            E.printStackTrace();
        }
    }

    public void removeTempTable(String tableName) {
        try {
            Statement s = connection.createStatement();
            String sql = "delete from Temp_Tables where Table_Name='" + tableName + "'";

            s.execute(sql);
            s.close();

        } catch (Exception E) {
            E.printStackTrace();
        }
    }

    public void setTempTableTimestamps(String tableName, java.sql.Timestamp minTS, java.sql.Timestamp maxTS) {
        try {
            Statement s = connection.createStatement();
            String sql = "update Temp_Tables set Min_TS='" + minTS.toString() + "', Max_TS='" + maxTS.toString()
                    + "' where Table_Name='" + tableName + "'";
            s.execute(sql);
            s.close();

        } catch (Exception E) {
            E.printStackTrace();
        }

    }

    public long getRecordCount(String date) {
        try {
            long count = 0;
            Statement s = connection.createStatement();
            String sql = "select * from Daily_Record_Counts where Entry_Date='" + date + "'";
            ResultSet rs = s.executeQuery(sql);
            if (rs.next()) {
                count = rs.getLong("Records_Processed");
            }
            rs.close();
            s.close();
            if (count < 0)
                count = 0;
            return count;
        } catch (Exception E) {
            E.printStackTrace();
        }
        return 0;
    }

    public long getExceededCount(String date) {
        try {
            long count = 0;
            Statement s = connection.createStatement();
            String sql = "select * from Daily_Record_Counts where Entry_Date='" + date + "'";
            ResultSet rs = s.executeQuery(sql);
            if (rs.next()) {
                count = rs.getLong("Records_Over");
            }
            rs.close();
            s.close();
            if (count < 0)
                count = 0;
            return count;
        } catch (Exception E) {
            E.printStackTrace();
        }
        return 0;
    }

    public void updateRecordCounts(String date, long recordCount, long exceededCount, java.sql.Timestamp exceededTS) {
        try {
            Statement s = connection.createStatement();

            // Try to bring the table up-to-date for version 1.1
            try {
                String sql = "alter table Daily_Record_Counts add column Threshold_TS timestamp";
                s.execute(sql);
            } catch (Exception e) {

            }

            try {
                // Try to insert first, if there is an entry this will fail and
                // force an update
                String tsSQL = "'" + exceededTS + "'";
                if (exceededTS == null)
                    tsSQL = "null";

                String sql = "insert into Daily_Record_Counts values ('" + date + "'," + recordCount + ", "
                        + exceededCount + "," + tsSQL + ")";
                s.execute(sql);
            } catch (Exception e) {
                String tsSQL = "";
                if (exceededTS != null)
                    tsSQL = ", Threshold_TS='" + exceededTS + "' ";

                // If the insert fails it indicates that a record exists (so
                // this is an update).
                String sql = "update Daily_Record_Counts set Records_Processed=" + recordCount + ", Records_Over="
                        + exceededCount + " " + tsSQL + " where Entry_Date='" + date + "'";
                s.execute(sql);
            }
            s.close();

        } catch (Exception E) {
            E.printStackTrace();
        }

    }

    /***************************************************************************
     * Creates an entry in the system metadata for a data file according to the
     * DataInfo parameter.
     * 
     * @param indexInfo
     *            Information about the data file being added
     */
    public void addData(DataInfo info) {
        try {
            Statement s = connection.createStatement();
            String sql = "insert into Data_Info values (" + info.getSensor() + ", " + 1 + ", '" + info.getYear() + "-"
                    + info.getMonth() + "-" + info.getDay() + "', " + info.getMonth() + ", " + info.getDay() + ", "
                    + info.getYear() + ", " + info.getRun() + ", '" + info.getDataPath() + "', " + info.getNumBytes()
                    + ", " + info.getNumRecords() + ", " + info.getMinTime() + ", " + info.getMaxTime() + ",null,null)";
            s.execute(sql);
            s.close();
        } catch (Exception E) {
            E.printStackTrace();
        }
    }

    /***************************************************************************
     * Removes a data file entry from the system metadata according to the given
     * parameters.
     * 
     * @param month
     *            Month of the index (1-12)
     * @param day
     *            Day of month of the index (1-31)
     * @param year
     *            Year of the index (YYYY)
     * @param run
     *            Run of the index (1-n)
     * @param sensor
     *            Sensor identifier of the index
     */
    public void removeData(int month, int day, int year, int run, int sensor) {
        try {
            Statement s = connection.createStatement();
            String sql = "delete from Data_Info where " + "Sensor_ID=" + sensor + " and " + "Run=" + run + " and "
                    + "Index_Date='" + year + "-" + month + "-" + day + "'";
            s.execute(sql);
            s.close();
        } catch (Exception E) {
            E.printStackTrace();
        }
    }

    /***************************************************************************
     * Adds a static index entry to the system metadata according to the
     * attributes of the IndexInfo parameter.
     * 
     * @param indexInfo
     *            Information to add to the system metadata
     */
    public void addStaticIndex(IndexInfo indexInfo) {
        try {
            Statement s = connection.createStatement();
            String sql = "insert into Static_Indices values (" + indexInfo.getSensorId() + ", " + indexInfo.getField()
                    + ", " + indexInfo.getRun() + ", '" + indexInfo.getYear() + "-" + indexInfo.getMonth() + "-"
                    + indexInfo.getDay() + "', " + indexInfo.getMonth() + ", " + indexInfo.getDay() + ", "
                    + indexInfo.getYear() + ", '" + indexInfo.getDbPath() + "', " + indexInfo.getMinTime() + ", "
                    + indexInfo.getMaxTime() + ")";
            s.execute(sql);
            s.close();
        } catch (Exception E) {
            E.printStackTrace();
        }
    }

    /***************************************************************************
     * Removes a static index entry from the system metadata according to the
     * given parameters.
     * 
     * @param nodeID
     *            Node identifier of where this index resides
     * @param sensor
     *            Sensor identifier of the index
     * @param month
     *            Month of the index (1-12)
     * @param day
     *            Day of month of the index (1-31)
     * @param year
     *            Year of the index (YYYY)
     * @param run
     *            Run of the index (1-n)
     */
    public void removeStaticIndex(int sensor, int field, int month, int day, int year, int run) {
        try {
            Statement s = connection.createStatement();
            String sql = "delete from Static_Indices where Sensor_ID=" + sensor + " and Field_ID=" + field + " and "
                    + "Run=" + run + " and " + "Index_Date='" + year + "-" + month + "-" + day + "'";
            s.execute(sql);
            s.close();
        } catch (Exception E) {
            E.printStackTrace();
        }
    }

}